package cron

import (
	dto2 "code.bydev.io/cht/customer/kyc_admin_service/internal/pkg/kycadminevent/dto"
	"code.bydev.io/cht/customer/kyc_admin_service/internal/utils/ukafka"
	"code.bydev.io/frameworks/byone/kafka"
	"context"
	"encoding/json"
	pbkyc "git.bybit.com/svc/stub/pkg/pb/api/kyc"

	"code.bydev.io/cht/customer/kyc-stub.git/pkg/bybit/kyc/common/enums"
	"code.bydev.io/cht/customer/kyc_admin_service/internal/model"
	"code.bydev.io/cht/customer/kyc_admin_service/internal/pkg/awss3"
	"code.bydev.io/cht/customer/kyc_admin_service/internal/pkg/biz"
	"code.bydev.io/cht/customer/kyc_admin_service/internal/pkg/config"
	"code.bydev.io/cht/customer/kyc_admin_service/internal/pkg/kycprovider"
	"code.bydev.io/cht/customer/kyc_admin_service/internal/pkg/kycprovider/dto"
	"code.bydev.io/cht/customer/kyc_admin_service/internal/pkg/kycprovider/provider"
	"code.bydev.io/cht/customer/kyc_dbstructure.git/pkg/o_kyc_action_image"
	"code.bydev.io/frameworks/byone/core/logc"
	"code.bydev.io/frameworks/byone/core/stores/redis"
	"code.bydev.io/frameworks/sechub-go/api"
	"github.com/spf13/cast"
	"go.uber.org/zap"
)

type Domain struct {
	logger          *zap.Logger
	adminRepo       biz.KycAdminRepo
	teller          provider.Teller
	redisClient     *redis.Redis
	secHub          *api.Sechub
	kycRecordCommon *model.KycRecordCommon
	kycActionState  *model.KycActionState
	kycActionImage  *model.KycActionImage
	kycProducer     *ukafka.ProducerClient
}

func NewDomain(
	log *zap.Logger,
	repo biz.KycAdminRepo,
	teller provider.Teller,
	redisClient *redis.Redis,
	secHub *api.Sechub,
	kycProducer *ukafka.ProducerClient,
) *Domain {
	return &Domain{
		logger:          log,
		adminRepo:       repo,
		teller:          teller,
		redisClient:     redisClient,
		secHub:          secHub,
		kycRecordCommon: &model.KycRecordCommon{},
		kycActionState:  &model.KycActionState{},
		kycActionImage:  &model.KycActionImage{},
		kycProducer:     kycProducer,
	}
}

func (d *Domain) ActionImageHandler(ctx context.Context) error {
	logc.Infow(ctx, "ActionImageHandler begin")
	limit := cast.ToInt(config.GetString("common.scan_size"))
	if limit == 0 {
		limit = 100
	}

	initRecords, err := d.adminRepo.GetInitRecords(ctx, limit)
	if err != nil {
		logc.Errorw(ctx, "s.adminRepo.GetInitRecords error", logc.Field("err", err))
		return err
	}
	if len(initRecords) == 0 {
		logc.Infow(ctx, "no init records")
		return nil
	}
	logc.Infow(ctx, "s.adminRepo.GetInitRecords info", logc.Field("initRecords", initRecords))
	for _, record := range initRecords {
		isDone, err := d.adminRepo.IsRecordDone(ctx, record.Id)
		if err != nil {
			logc.Errorw(ctx, "s.adminRepo.IsRecordDone error", logc.Field("err", err))
			continue
		}
		if isDone {
			logc.Infow(ctx, "s.adminRepo.IsRecordDone is done", logc.Field("record", record))
			continue
		}
		err = d.handleImage(ctx, record)
		if err != nil {
			logc.Errorw(ctx, "s.handleImage error", logc.Field("record", record), logc.Field("err", err))
			continue
		}
		logc.Infow(ctx, "s.handleImage success", logc.Field("record", record))
	}
	logc.Infow(ctx, "ActionImageHandler end")
	return nil
}

func (d *Domain) handleImage(ctx context.Context, record *o_kyc_action_image.Record) error {
	logc.Infow(ctx, "handleImage begin")
	// 如果不存在，取得圖片並上傳
	actionImg := &dto.KYCActionImage{
		IdempotentID: record.IdempotentId,
		MemberID:     record.MemberId,
		Provider:     enums.KYCProvider(record.Provider),
		AccountID:    record.AccountId,
		RecordID:     record.RecordId,
		ImgID:        record.ImgId,
		ImgType:      dto.ImgType(record.ImgType),
	}
	// 取得圖片並上傳
	doc, err := d.getImageRawAndUpload(ctx, actionImg)
	if err != nil {
		logc.Errorw(ctx, "s.GetImageRawAndUpload error", logc.Field("err", err))
		return err
	}
	logc.Infow(ctx, "s.GetImageRawAndUpload", logc.Field("doc", doc))
	// 更新 DB
	err = d.adminRepo.ActionImageComplete(ctx, record, doc)
	if err != nil {
		logc.Errorw(ctx, "ActionImageComplete error", logc.Field("err", err))
		return err
	}
	logc.Infow(ctx, "handleImage end")
	return nil
}

func (d *Domain) getImageRawAndUpload(ctx context.Context, actionImg *dto.KYCActionImage) (*dto.Document, error) {
	logc.Infow(ctx, "getImageRawAndUpload begin")
	kycProvider := kycprovider.NewProvider(d.logger, dto.KYCProvider(actionImg.Provider), d.teller)
	s3 := awss3.New(d.logger)
	// 取得圖片
	doc, err := kycProvider.GetImageRaw(ctx, actionImg)
	if err != nil {
		logc.Errorw(ctx, "GetImageRaw error", logc.Field("err", err))
		return nil, err
	}
	if doc == nil {
		logc.Errorw(ctx, "GetImageRaw failed", logc.Field("doc", doc))
		return nil, nil
	}
	// 上傳圖片
	err = s3.Upload(ctx, actionImg.IdempotentID, doc)
	if err != nil {
		logc.Errorw(ctx, "Upload error", logc.Field("err", err))
		return nil, err
	}
	logc.Infow(ctx, "getImageRawAndUpload end")

	// 挂载上传至filecore的生产者
	err = d.filecoreUploadNotify(ctx, actionImg.MemberID, actionImg.IdempotentID, actionImg.ImgType)
	if err != nil {
		logc.Errorw(ctx, "filecoreUploadNotify error", logc.Field("err", err))
		return nil, err
	}
	return doc, nil
}

// filecoreUploadNotify 图片文件上传filecore任务
func (d *Domain) filecoreUploadNotify(ctx context.Context, memberID int64, idempotentID string, imgType dto.ImgType) error {
	logc.Infow(ctx, "filecoreUploadNotify",
		logc.Field("memberID", memberID),
		logc.Field("idempotentID", idempotentID),
		logc.Field("imgType", imgType),
	)

	if imgType < dto.ImgTypeDocFront || imgType > dto.ImgTypeFace {
		logc.Infow(ctx, "invalid imgType", logc.Field("idempotentID", idempotentID), logc.Field("imgType", imgType))
		return nil
	}

	// 当前只允许issue country == TUR 的身份证, 可以上传到s3 (满足Narkasa photoSharing诉求)
	// 当前只允许证件正反面、自拍上传
	record, err := d.adminRepo.GetActionByIdempotentID(ctx, idempotentID)
	if err != nil {
		logc.Errorw(ctx, "GetActionByIdempotentID error", logc.Field("err", err))
		return err
	}
	if !(record.Decision == 1 && record.Country == "TUR" && record.DocType == 1) {
		logc.Infow(ctx, "not allowed to upload to filecore",
			logc.Field("idempotentID", idempotentID),
			logc.Field("actionRecord", record),
		)
		return nil
	}

	fileBizType := pbkyc.FileBizType(imgType + 1)
	bytes, _ := json.Marshal(&dto2.FilecoreUploadNotify{
		MemberID:     memberID,
		IdempotentID: idempotentID,
		FileBizType:  fileBizType,
	})
	err = d.kycProducer.FilecoreUpload.Send(ctx, &kafka.Message{Value: bytes})
	if err != nil {
		logc.Errorw(ctx, "send FilecoreUploadNotify error", logc.Field("err", err))
		return err
	}

	logc.Infow(ctx, "filecoreUploadNotify Success",
		logc.Field("memberID", memberID),
		logc.Field("idempotentID", idempotentID),
		logc.Field("imgType", imgType),
	)

	return nil
}
